use tracing::debug;

use crate::ast_define::MetaInfo;

/// 语义错误类型，对应lab3要求中的11种错误类型
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum SemanticErrorType {
    /// 1. 变量未声明：使用了没有声明的变量
    ///
    /// - 变量仅包含三种类型：int，array，function
    /// - int 类型的变量在声明时可以定义也可以不定义，即
    ///     -   ```sysy
    ///         int a = 1; //正确
    ///         ```
    ///     -   ```sysy
    ///         int a; //正确
    ///         ```
    ///     -   ```sysy
    ///         int a = 1;
    ///         int a; //错误
    ///         ```
    /// - 对于 array 类型的变量，本次实验不要求对数组定义语句的右值进行语义检查：
    ///     ```sysy
    ///     int a[2] = {1, 2}; // 测试用例中如出现数组定义，保证等号右边一定合法
    ///     ```
    /// - 部分参与者在实操时容易犯以下错误
    ///     - 只检查当前的作用域中有无对该变量的定义，而误报未定义的变量
    ///      ```sysy
    ///     int main() {
    ///         int a = 1;
    ///         if (cond1) {
    ///         int b = a; // 误报 a 未定义
    ///         }
    ///     }
    ///     ```
    ///     - 全局作用域中存在以 a 为命名的函数，局部有以 a 命名的变量,
    ///       此处变量 a 的类型是 int 而非 func
    ///     ```sysy
    ///     int a() {}
    ///     int main() {
    ///         int a = 1;
    ///         int b = a(a); // 报错(应报告类型 10，该变量非函数)
    ///     }
    ///     ```
    UndefinedVariable = 1,
    /// 2. 函数未定义：使用了没有声明和定义的函数
    UndefinedFunction = 2,
    /// 3. 变量重复声明：
    ///     - 声明全局变量时与函数同名
    ///     - 声明全局变量时与已声明的全局变量同名
    ///     - 声明局部变量与已声明的相同作用域的局部变量同名
    ///
    /// !该错误的恢复方式同函数重复定义, 即跳过整个变量声明进行错误恢复
    ///   - 对于函数参数与已有参数重名，在报告错误类型 3 后，
    ///     仅抛弃重复的参数进行恢复，如:
    ///      ```sysy
    ///      int f(int i, int i) { return 0; }
    ///      ```
    ///      应报错:
    ///      ```bash
    ///      Error type 3 at Line 1: Redefined variable: i.
    ///      ```
    ///      并且将其恢复为
    ///      ```sysy
    ///      int f(int i) { return 0; }
    ///      ```
    RedefinedVariable = 3,
    /// 4. 函数重复定义：
    ///    - 定义函数时与已声明的全局变量同名
    ///    - 定义函数时与已定义的函数同名
    ///
    /// !该错误请跳过整个函数进行错误恢复
    RedefinedFunction = 4,
    ///  5. 赋值号两侧类型不匹配：赋值号两侧的类型不相同
    ///
    /// 我们允许如下形式的多维数组间互相赋值：
    /// ```sysy
    /// int a[3][3][3];
    /// int b[2][2];
    ///
    /// a[1] = b; // 你可以将 a[1]视作一个二维数组 int[3][3]，
    /// // 这种赋值我们是合法的，
    /// // 也不会检查维度上长度不一致（3 和 2 不一致）的问题
    ///
    /// a[1][1] = b; //这种是不合法的，因为维度不一致
    /// ```
    TypeMismatchedForAssignment = 5,
    /// 6. 运算符需求类型与提供类型不匹配：运算符需要的类型为 int 却提供 array 或 function 等
    ///     - 如果一个表达式的第一个操作数已经和运算符不匹配，
    ///       就直接报错类型 6，不要再访问其它的操作数.
    ///     - 因此，类似 `arr + arr`，`func - func` 的表达式只报告一次错误类型 6.
    TypeMismatchedForOperands = 6,
    /// 7. 返回值类型不匹配
    TypeMismatchedForReturn = 7,
    /// 8. 函数参数不适用
    ///
    /// 为了降低实验难度，
    /// 我们保证测试用例中的函数参数不会为多维（二维及以上）数组。
    FunctionNotApplicableForArguments = 8,
    /// 9. 对非数组使用下标运算符
    NotAnArray = 9,
    /// 10. 对变量使用函数调用
    NotAFunction = 10,
    /// 11. 赋值号左侧非变量或数组元素
    InvalidLeftHandSideOfAssignment = 11,
}

/// 语义错误结构体
#[derive(Debug, Clone)]
pub struct SemanticError {
    pub error_type: SemanticErrorType,
    pub message: String,
    pub location: MetaInfo,
}

impl SemanticError {
    pub fn new(error_type: SemanticErrorType, message: String, location: MetaInfo) -> Self {
        Self {
            error_type,
            message,
            location,
        }
    }

    /// 创建变量未声明错误
    #[tracing::instrument(skip_all)]
    pub fn undefined_variable(var_name: &str, location: MetaInfo) -> Self {
        debug!("创建变量未声明错误: {var_name}");
        Self::new(
            SemanticErrorType::UndefinedVariable,
            format!("Undefined variable: {}.", var_name),
            location,
        )
    }

    /// 创建函数未定义错误
    #[tracing::instrument(skip_all)]
    pub fn undefined_function(func_name: &str, location: MetaInfo) -> Self {
        debug!("创建函数未定义错误: {func_name}");
        Self::new(
            SemanticErrorType::UndefinedFunction,
            format!("Undefined function: {}.", func_name),
            location,
        )
    }

    /// 创建变量重复声明错误
    #[tracing::instrument(skip_all)]
    pub fn redefined_variable(var_name: &str, location: MetaInfo) -> Self {
        debug!("创建变量重复声明错误: {var_name}");
        Self::new(
            SemanticErrorType::RedefinedVariable,
            format!("Redefined variable: {}.", var_name),
            location,
        )
    }

    /// 创建函数重复定义错误
    #[tracing::instrument(skip_all)]
    pub fn redefined_function(func_name: &str, location: MetaInfo) -> Self {
        debug!("创建函数重复定义错误: {func_name}");
        Self::new(
            SemanticErrorType::RedefinedFunction,
            format!("Redefined function: {}.", func_name),
            location,
        )
    }

    /// 创建赋值类型不匹配错误
    #[tracing::instrument(skip_all)]
    pub fn type_mismatched_for_assignment(location: MetaInfo) -> Self {
        debug!("创建赋值类型不匹配错误");
        Self::new(
            SemanticErrorType::TypeMismatchedForAssignment,
            "Type mismatched for assignment.".to_string(),
            location,
        )
    }

    /// 创建运算符类型不匹配错误
    #[tracing::instrument(skip_all)]
    pub fn type_mismatched_for_operands(location: MetaInfo) -> Self {
        debug!("创建运算符类型不匹配错误");
        Self::new(
            SemanticErrorType::TypeMismatchedForOperands,
            "Type mismatched for operands.".to_string(),
            location,
        )
    }

    /// 创建返回值类型不匹配错误
    #[tracing::instrument(skip_all)]
    pub fn type_mismatched_for_return(location: MetaInfo) -> Self {
        debug!("创建返回值类型不匹配错误");
        Self::new(
            SemanticErrorType::TypeMismatchedForReturn,
            "Type mismatched for return.".to_string(),
            location,
        )
    }

    /// 创建函数参数不适用错误
    #[tracing::instrument(skip_all)]
    pub fn function_not_applicable_for_arguments(location: MetaInfo) -> Self {
        debug!("创建函数参数不适用错误");
        Self::new(
            SemanticErrorType::FunctionNotApplicableForArguments,
            "Function is not applicable for arguments.".to_string(),
            location,
        )
    }

    /// 创建对非数组使用下标运算符错误
    #[tracing::instrument(skip_all)]
    pub fn not_an_array(var_name: &str, location: MetaInfo) -> Self {
        debug!("创建对非数组使用下标运算符错误: {var_name}");
        Self::new(
            SemanticErrorType::NotAnArray,
            format!("Not an array: {}.", var_name),
            location,
        )
    }

    /// 创建对变量使用函数调用错误
    #[tracing::instrument(skip_all)]
    pub fn not_a_function(var_name: &str, location: MetaInfo) -> Self {
        debug!("创建对变量使用函数调用错误: {var_name}");
        Self::new(
            SemanticErrorType::NotAFunction,
            format!("Not a function: {}.", var_name),
            location,
        )
    }

    /// 创建赋值号左侧非变量或数组元素错误
    #[tracing::instrument(skip_all)]
    pub fn invalid_left_hand_side_of_assignment(location: MetaInfo) -> Self {
        debug!("创建赋值号左侧非变量或数组元素错误");
        Self::new(
            SemanticErrorType::InvalidLeftHandSideOfAssignment,
            "The left-hand side of an assignment must be a variable.".to_string(),
            location,
        )
    }
}
